1 /** 2 The meta module contains: 3 $(TOC opAAKey) 4 $(TOC GetUniqueConstraintStructNames) 5 $(TOC GetMembersWithUDA) 6 $(TOC hasMembersWithUDA) 7 $(TOC createConstraintStructs) 8 $(TOC GetForeignKeys) 9 $(TOC hasForeignKeys) 10 $(TOC GetForeignKeyRefTable) 11 $(TOC GetDefault) 12 $(TOC hasDefault) 13 $(TOC createForeignKeyPropertyConverter) 14 $(TOC createForeignKeyProperties) 15 $(TOC createForeignKeyCheckExceptions) 16 $(TOC createForeignKeyChanged) 17 $(TOC hasExclusionConstraints) 18 $(TOC GetExclusionConstraints) 19 20 License: $(GPL2) 21 22 Authors: Matthew Armbruster 23 24 $(B Source:) $(SRC $(SRCFILENAME)) 25 26 Copyright: 2016 27 */ 28 module db_constraints.utils.meta; 29 30 import std.meta : NoDuplicates, AliasSeq; 31 import std.traits : isInstanceOf, hasUDA; 32 33 import db_constraints.constraints; 34 /** 35 Used in KeyedItem for the generated structs. 36 This allows the struct to be used as a key 37 in an associative array. 38 39 The template loops over the members to define 40 toHash, opEquals, and opCmp for the struct. 41 */ 42 mixin template opAAKey(T) 43 if (is(T == struct)) 44 { 45 // Gets the hash code of the struct by looping over the members. 46 final size_t toHash() const nothrow @safe 47 { 48 size_t result; 49 foreach(i, dummy; this.tupleof) 50 { 51 if (i == 0) 52 { 53 result = typeid(this.tupleof[i]).getHash(&this.tupleof[i]); 54 } 55 else 56 { 57 result ^= typeid(this.tupleof[i]).getHash(&this.tupleof[i]); 58 } 59 } 60 return result; 61 } 62 // Checks each member to determine if the structs are equal. 63 final bool opEquals(inout(T) pk) const pure nothrow @nogc @safe 64 { 65 bool result; 66 foreach(i, dummy; pk.tupleof) 67 { 68 if (this.tupleof[i] == pk.tupleof[i]) 69 { 70 result = true; 71 continue; 72 } 73 else if (this.tupleof[i] != pk.tupleof[i]) 74 { 75 result = false; 76 break; 77 } 78 assert(0); 79 } 80 return result; 81 } 82 // Compares each member and returns the result. 83 final int opCmp(inout(T) pk) const pure nothrow @nogc @safe 84 { 85 int result; 86 foreach(i, dummy; pk.tupleof) 87 { 88 if (this.tupleof[i] > pk.tupleof[i]) 89 { 90 result = 1; 91 break; 92 } 93 else if (this.tupleof[i] < pk.tupleof[i]) 94 { 95 result = -1; 96 break; 97 } 98 else if (this.tupleof[i] == pk.tupleof[i]) 99 { 100 result = 0; 101 continue; 102 } 103 assert(0); 104 } 105 return result; 106 } 107 } 108 109 /** 110 Gets the names given to the different UniqueConstraints for ClassName. 111 The UniqueConstraintColumns are usually put on getters and setters. 112 Returns: 113 AliasSeq of all the distinct UniqueConstraintColumn.name in ClassName 114 */ 115 template GetUniqueConstraintStructNames(ClassName) 116 { 117 template Impl(T...) 118 { 119 static if (T.length == 0) 120 { 121 alias Impl = AliasSeq!(); 122 } 123 else 124 { 125 static if (__traits(compiles, __traits(getMember, ClassName, T[0]))) 126 { 127 alias Impl = AliasSeq!(Overloads!(__traits(getOverloads, ClassName, T[0])), Impl!(T[1 .. $])); 128 } 129 else 130 { 131 alias Impl = AliasSeq!(Impl!(T[1 .. $])); 132 } 133 } 134 } 135 template Overloads(S...) 136 { 137 static if (S.length == 0) 138 { 139 alias Overloads = AliasSeq!(); 140 } 141 else 142 { 143 enum attributes = Get!(__traits(getAttributes, S[0])); 144 static if (attributes == "") 145 { 146 alias Overloads = AliasSeq!(Overloads!(S[1 .. $])); 147 } 148 else 149 { 150 alias Overloads = AliasSeq!(attributes, Overloads!(S[1 .. $])); 151 } 152 } 153 } 154 template Get(P...) 155 { 156 static if (P.length == 0) 157 { 158 enum Get = ""; 159 } 160 else 161 { 162 static if (isInstanceOf!(UniqueConstraintColumn, P[0])) 163 { 164 alias Get = P[0].name; 165 } 166 else 167 { 168 alias Get = Get!(P[1 .. $]); 169 } 170 } 171 } 172 alias GetUniqueConstraintStructNames = NoDuplicates!(Impl!(__traits(derivedMembers, ClassName))); 173 } 174 175 /** 176 Gets the properties of ClassName marked with @attribute. If the 177 attribute is PrimaryKeyColumn then it also confirms the property has 178 NotNull. 179 Returns: 180 AliasSeq with distinct properties that have @attribute assigned to it. 181 */ 182 template GetMembersWithUDA(ClassName, attribute) 183 { 184 template Impl(T...) 185 { 186 static if (T.length == 0) 187 { 188 alias Impl = AliasSeq!(); 189 } 190 else 191 { 192 static if (__traits(compiles, __traits(getMember, ClassName, T[0])) && 193 Overloads!(__traits(getOverloads, ClassName, T[0]))) 194 { 195 alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $])); 196 } 197 else 198 { 199 alias Impl = AliasSeq!(Impl!(T[1 .. $])); 200 } 201 } 202 } 203 template Overloads(P...) 204 { 205 static if (P.length == 0) 206 { 207 enum Overloads = false; 208 } 209 else static if (hasUDA!(P[0], attribute)) 210 { 211 static if (__traits(isSame, attribute, PrimaryKeyColumn)) 212 { 213 static assert(hasUDA!(P[0], NotNull), 214 "Primary key columns must have the NotNull" ~ 215 " attribute which is missing from the class " ~ 216 ClassName.stringof); 217 } 218 enum Overloads = true; 219 } 220 else 221 { 222 alias Overloads = Overloads!(P[1 .. $]); 223 } 224 } 225 226 alias GetMembersWithUDA = NoDuplicates!(Impl!(__traits(derivedMembers, ClassName))); 227 } 228 229 /** 230 Confirms there are members in ClassName with @attribute. 231 Returns: 232 true if there are members that have @attribute 233 */ 234 template hasMembersWithUDA(ClassName, attribute) 235 { 236 template Impl(T...) 237 { 238 static if (T.length == 0) 239 { 240 enum Impl = false; 241 } 242 else 243 { 244 static if (__traits(compiles, __traits(getMember, ClassName, T[0])) && 245 Overloads!(__traits(getOverloads, ClassName, T[0]))) 246 { 247 enum Impl = true; 248 } 249 else 250 { 251 alias Impl = Impl!(T[1 .. $]); 252 } 253 } 254 } 255 template Overloads(P...) 256 { 257 static if (P.length == 0) 258 { 259 enum Overloads = false; 260 } 261 else static if (hasUDA!(P[0], attribute)) 262 { 263 enum Overloads = true; 264 } 265 else 266 { 267 alias Overloads = Overloads!(P[1 .. $]); 268 } 269 } 270 271 enum hasMembersWithUDA = Impl!(__traits(derivedMembers, ClassName)); 272 } 273 274 /* 275 Using the ClassName and ClusteredIndexAttributeName, createConstraintStructs will 276 append together strings using $(SRCTAG GetUniqueConstraintStructNames) and $(SRCTAG GetMembersWithUDA) 277 that make up the structs used as your unique keys. 278 Returns: 279 A string full of the structs for ClassName that make each row unique. 280 */ 281 template createConstraintStructs(ClassName, string ClusteredIndexAttributeName) 282 { 283 string createConstraintStructs() 284 { 285 string result = "public:\n"; 286 foreach(name; GetUniqueConstraintStructNames!(ClassName)) 287 { 288 static if (name == ClusteredIndexAttributeName) 289 { 290 result ~= " final alias " ~ name ~ " = ClusteredIndex;\n"; 291 result ~= " final alias " ~ name ~ "_key = key;\n"; 292 } 293 else 294 { 295 result ~= " final struct " ~ name ~ "\n"; 296 result ~= " {\n"; 297 foreach(columnName; GetMembersWithUDA!(ClassName, UniqueConstraintColumn!name)) 298 { 299 result ~= " typeof(" ~ ClassName.stringof ~ "._" ~ columnName ~ ") " ~ columnName ~ ";\n"; 300 } 301 result ~= " mixin opAAKey!(" ~ name ~ ");\n"; 302 result ~= " }\n"; 303 result ~= " final @property " ~ name ~ " " ~ name ~ "_key() const nothrow pure @safe @nogc\n"; 304 result ~= " {\n"; 305 result ~= " auto _" ~ name ~ "_key = " ~ name ~ "();\n"; 306 foreach(columnName; GetMembersWithUDA!(ClassName, UniqueConstraintColumn!name)) 307 { 308 result ~= " _" ~ name ~ "_key." ~ columnName ~ " = this._" ~ columnName ~ ";\n"; 309 } 310 result ~= " return _" ~ name ~ "_key;\n"; 311 result ~= " }\n"; 312 } 313 } 314 return result; 315 } 316 } 317 318 /** 319 Gets all of the $(WIKI constraints, ForeignKey) that ClassName is attributed with. If 320 the foreign key name is left blank then the default name is $(D "fk_" ~ ClassName ~ "__" ~ referencedClassName). 321 */ 322 template GetForeignKeys(ClassName) 323 { 324 template Impl(T...) 325 { 326 static if (T.length == 0) 327 { 328 alias Impl = AliasSeq!(); 329 } 330 else static if (isInstanceOf!(ForeignKey, T[0])) 331 { 332 static if (T[0].name == "") 333 { 334 enum name = "fk_" ~ ClassName.stringof ~ "_" ~ T[0].referencedTableName; 335 alias R = ForeignKey!(name, T[0].columnNames, T[0].referencedTableName, T[0].referencedColumnNames, T[0].updateRule, T[0].deleteRule); 336 alias Impl = AliasSeq!(R, Impl!(T[1 .. $])); 337 } 338 else 339 { 340 alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $])); 341 } 342 } 343 else 344 { 345 alias Impl = Impl!(T[1 .. $]); 346 } 347 } 348 alias GetForeignKeys = Impl!(__traits(getAttributes, ClassName)); 349 } 350 351 /** 352 Confirms ClassName has foreign keys. 353 Returns: 354 true if ClassName has an instance of @ForeignKey 355 */ 356 template hasForeignKeys(ClassName) 357 { 358 template Impl(T...) 359 { 360 static if (T.length == 0) 361 { 362 enum Impl = false; 363 } 364 else static if (isInstanceOf!(ForeignKey, T[0])) 365 { 366 enum Impl = true; 367 } 368 else 369 { 370 alias Impl = Impl!(T[1 .. $]); 371 } 372 } 373 enum hasForeignKeys = Impl!(__traits(getAttributes, ClassName)); 374 } 375 376 /** 377 Gets all of the referenced foreign keys for ClassName. 378 Returns: 379 Distinct list of all referenced classes for ClassName. 380 */ 381 template GetForeignKeyRefTable(ClassName) 382 if (hasForeignKeys!(ClassName)) 383 { 384 template Impl(T...) 385 { 386 static if (T.length == 0) 387 { 388 alias Impl = AliasSeq!(); 389 } 390 else static if (isInstanceOf!(ForeignKey, T[0])) 391 { 392 enum attributes = T[0].referencedTableName; 393 alias Impl = AliasSeq!(attributes, Impl!(T[1 .. $])); 394 } 395 else 396 { 397 alias Impl = Impl!(T[1 .. $]); 398 } 399 } 400 alias GetForeignKeyRefTable = NoDuplicates!(Impl!(__traits(getAttributes, ClassName))); 401 } 402 403 /** 404 Gets the value for ClassName.memberName inside of @Default!(value) if memberName has @Default!(value) 405 */ 406 template GetDefault(ClassName, string memberName) 407 if (hasDefault!(ClassName, memberName)) 408 { 409 static if (__traits(compiles, __traits(getMember, ClassName, memberName))) 410 { 411 alias GetDefault = Overloads!(__traits(getOverloads, ClassName, memberName)); 412 } 413 else 414 { 415 alias GetDefault = AliasSeq!(); 416 } 417 template Overloads(S...) 418 { 419 import std.conv : to; 420 static if (S.length == 0) 421 { 422 alias Overloads = AliasSeq!(); 423 } 424 else 425 { 426 enum attributes = Get!(__traits(getAttributes, S[0])); 427 static if (attributes.to!string == "") 428 { 429 alias Overloads = Overloads!(S[1 .. $]); 430 } 431 else 432 { 433 alias Overloads = attributes; 434 } 435 } 436 } 437 template Get(P...) 438 { 439 static if (P.length == 0) 440 { 441 enum string Get = ""; 442 } 443 else static if (isInstanceOf!(Default, P[0])) 444 { 445 enum Get = P[0].value; 446 } 447 else 448 { 449 alias Get = Get!(P[1 .. $]); 450 } 451 } 452 } 453 454 /** 455 Confirms ClassName.memberName has @Default!(value) 456 Returns: 457 true if ClassName.memberName has @Default!(value) 458 */ 459 template hasDefault(ClassName, string memberName) 460 { 461 static if (__traits(compiles, __traits(getMember, ClassName, memberName))) 462 { 463 enum hasDefault = Overloads!(__traits(getOverloads, ClassName, memberName)); 464 } 465 else 466 { 467 enum hasDefault = false; 468 } 469 template Overloads(S...) 470 { 471 static if (S.length == 0) 472 { 473 enum Overloads = false; 474 } 475 else 476 { 477 enum attributes = Get!(__traits(getAttributes, S[0])); 478 static if (attributes) 479 { 480 enum Overloads = true; 481 } 482 else 483 { 484 alias Overloads = Overloads!(S[1 .. $]); 485 } 486 } 487 } 488 template Get(P...) 489 { 490 static if (P.length == 0) 491 { 492 enum Get = false; 493 } 494 else static if (isInstanceOf!(Default, P[0])) 495 { 496 enum Get = true; 497 } 498 else 499 { 500 alias Get = Get!(P[1 .. $]); 501 } 502 } 503 } 504 /** 505 Creates the foreign key properties inside of $(D KeyedItem) 506 that convert the keyed items properties into the necessary 507 foreign key clustered index. 508 */ 509 template createForeignKeyPropertyConverter(ClassName) 510 { 511 string createForeignKeyPropertyConverter() 512 { 513 string result = ""; 514 foreach(foreignKey; GetForeignKeys!(ClassName)) 515 { 516 result ~= "final bool " ~ foreignKey.name ~ "_key(out " ~ foreignKey.referencedTableName ~ ".key_type aKey)\n"; 517 result ~= "{\n"; 518 result ~= " bool result;\n"; 519 result ~= " static if (\n"; 520 for (int i = 0; i < foreignKey.columnNames.length; ++i) 521 { 522 if (i > 0) 523 { 524 result ~= " &&\n"; 525 } 526 result ~= " is(typeof(aKey." ~ foreignKey.referencedColumnNames[i] ~ ") == typeof(this." ~ foreignKey.columnNames[i] ~ "))"; 527 } 528 result ~= "\n"; 529 result ~= " )\n"; 530 result ~= " {\n"; 531 for (int i = 0; i < foreignKey.columnNames.length; ++i) 532 { 533 result ~= " aKey." ~ foreignKey.referencedColumnNames[i] ~ " = this." ~ foreignKey.columnNames[i] ~ ";\n"; 534 } 535 result ~= " result = true;\n"; 536 result ~= " }\n"; 537 result ~= " else static if (__traits(compiles,\n"; 538 result ~= " (" ~ ClassName.stringof ~ " b)\n"; 539 result ~= " {\n"; 540 foreach(columnName; foreignKey.columnNames) 541 { 542 result ~= " if (b." ~ columnName ~ ".isNull == true) { }\n"; 543 } 544 result ~= " }))\n"; 545 result ~= " {\n"; 546 result ~= " if (\n"; 547 foreach(columnName; foreignKey.columnNames) 548 { 549 if (columnName != foreignKey.columnNames[0]) 550 { 551 result ~= " &&\n"; 552 } 553 result ~= " !this." ~ columnName ~ ".isNull"; 554 } 555 result ~= "\n"; 556 result ~= " )\n"; 557 result ~= " {\n"; 558 for (int i = 0; i < foreignKey.columnNames.length; ++i) 559 { 560 result ~= " aKey." ~ foreignKey.referencedColumnNames[i] ~ " = this." ~ foreignKey.columnNames[i] ~ ";\n"; 561 } 562 result ~= " result = true;\n"; 563 result ~= " }\n"; 564 result ~= " else\n"; 565 result ~= " {\n"; 566 result ~= " result = false;\n"; 567 result ~= " }\n"; 568 result ~= " }\n"; 569 result ~= " else\n"; 570 result ~= " {\n"; 571 result ~= " static assert(false, \"Column type mismatch for "~ foreignKey.name ~ ".\");\n"; 572 result ~= " }\n"; 573 result ~= " return result;\n"; 574 result ~= "}\n"; 575 } 576 return result; 577 } 578 } 579 580 /** 581 Creates the foreign key properties that will be used in KeyedCollection. 582 It loops over all of the foreign key attributes for ClassName and creates 583 a write-only property for each referenced class by using the class' name 584 in lower case. There is a static assert that makes sure the lower case 585 class name does not equal the class name. This would result in name 586 collisions and is not conforming to the D style. 587 588 There are two setters that are made. One takes the referenced class 589 by reference and the other accepts null. The null setter removes the 590 reference and disconnects the emitted signals. The setter that takes 591 the class by reference connects the emitted signals and keeps the 592 address of the class to check foreign key constraints when anything 593 changes. 594 */ 595 template createForeignKeyProperties(ClassName) 596 { 597 string createForeignKeyProperties() 598 { 599 import std.uni : toLower; 600 string result = ""; 601 foreach(member; GetForeignKeyRefTable!(ClassName)) 602 { 603 static assert(member != member.toLower, "The class " ~ member ~ " should start with a capital letter to use Foreign Keys or else there will be name collisions."); 604 result ~= "private " ~ member ~ " *_" ~ member.toLower ~ ";\n"; 605 result ~= "private " ~ member ~ ".key_type _changed" ~ member ~ "Row;\n"; 606 607 result ~= "final @property void " ~ member.toLower ~ "(ref " ~ member ~ " " ~ member.toLower ~ "_)\n"; 608 result ~= "{\n"; 609 result ~= " this." ~ member.toLower ~ " = null;\n"; 610 result ~= " this._" ~ member.toLower ~ " = &" ~ member.toLower ~ "_;\n"; 611 foreach(foreignKey; GetForeignKeys!(ClassName)) 612 { 613 static if (foreignKey.referencedTableName == member) 614 { 615 result ~= " this._" ~ member.toLower ~ ".collectionChanged.connect(&" ~ foreignKey.name ~ "_Changed);\n"; 616 } 617 } 618 result ~= " checkForeignKeys();\n"; 619 result ~= "}\n"; 620 621 622 result ~= "final @property void " ~ member.toLower ~ "(typeof(null) n)\n"; 623 result ~= "{\n"; 624 result ~= " if (this._" ~ member.toLower ~ " !is null)\n"; 625 result ~= " {\n"; 626 foreach(foreignKey; GetForeignKeys!(ClassName)) 627 { 628 static if (foreignKey.referencedTableName == member) 629 { 630 result ~= " this._" ~ member.toLower ~ ".collectionChanged.disconnect(&" ~ foreignKey.name ~ "_Changed);\n"; 631 } 632 } 633 result ~= " this._" ~ member.toLower ~ " = null;\n"; 634 result ~= " }\n"; 635 result ~= "}\n"; 636 } 637 return result; 638 } 639 } 640 641 /** 642 Creates the foreign key check exceptions by seeing if the foreign key has been associated and 643 whether or not the referenced table has a matching record. 644 */ 645 template createForeignKeyCheckExceptions(ClassName) 646 { 647 string createForeignKeyCheckExceptions() 648 { 649 import std.uni : toLower; 650 string result = ""; 651 foreach(foreignKey; GetForeignKeys!(ClassName)) 652 { 653 static assert(foreignKey.referencedTableName != foreignKey.referencedTableName.toLower, "The class " ~ member ~ " should start with a capital letter to use Foreign Keys or else there will be name collisions."); 654 result ~= "if (this._" ~ foreignKey.referencedTableName.toLower ~ " !is null)\n"; 655 result ~= "{\n"; 656 result ~= " " ~ foreignKey.referencedTableName ~ ".key_type i;\n"; 657 result ~= " if(a." ~ foreignKey.name ~ "_key(i))\n"; 658 result ~= " {\n"; 659 result ~= " enforceEx!ForeignKeyException(this._" ~ foreignKey.referencedTableName.toLower ~ ".contains(i), \"" ~ foreignKey.name ~ " violation.\");\n"; 660 result ~= " }\n"; 661 result ~= "}\n"; 662 } 663 return result; 664 } 665 } 666 667 /** 668 Creates the foreign keys update rule and delete rule property and sets them to the foreign key attribute property. 669 It also creates the function that will be attached to the foreign key when it is associated. This is where 670 the update rule and delete rule are used since the referenced class will emit what changed and to which item. 671 */ 672 template createForeignKeyChanged(ClassName) 673 { 674 string createForeignKeyChanged() 675 { 676 import std.conv : to; 677 string result = ""; 678 foreach(foreignKey; GetForeignKeys!(ClassName)) 679 { 680 result ~= "Rule " ~ foreignKey.name ~ "_UpdateRule = Rule." ~ foreignKey.updateRule.to!string ~ ";\n"; 681 result ~= "Rule " ~ foreignKey.name ~ "_DeleteRule = Rule." ~ foreignKey.deleteRule.to!string ~ ";\n"; 682 result ~= "void " ~ foreignKey.name ~ "_Changed(string propertyName, " ~ foreignKey.referencedTableName ~ ".key_type item_key)\n"; 683 result ~= "{\n"; 684 result ~= " if (canFind(" ~ foreignKey.referencedColumnNames.to!string ~ ", propertyName))\n"; 685 result ~= " {\n"; 686 result ~= " this._changed" ~ foreignKey.referencedTableName ~ "Row = item_key;\n"; 687 result ~= " }\n"; 688 result ~= " else if (propertyName == \"key\")\n"; 689 result ~= " {\n"; 690 // onUpdate 691 result ~= " auto changed" ~ foreignKey.referencedTableName ~ " = this.byValue.filter!(\n"; 692 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 693 result ~= " {\n"; 694 result ~= " " ~ foreignKey.referencedTableName ~ ".key_type i;\n"; 695 result ~= " return (a." ~ foreignKey.name ~ "_key(i) ? i == this._changed" ~ foreignKey.referencedTableName ~ "Row : false);\n"; 696 result ~= " }());\n"; 697 result ~= " final switch (" ~ foreignKey.name ~ "_UpdateRule) with (Rule)\n"; 698 result ~= " {\n"; 699 result ~= " case noAction:\n"; 700 result ~= " break;\n"; 701 result ~= " case restrict:\n"; 702 result ~= " if (!changed" ~ foreignKey.referencedTableName ~ ".empty)\n"; 703 result ~= " throw new ForeignKeyException(\"" ~ foreignKey.name ~ " violation.\");\n"; 704 result ~= " break;\n"; 705 result ~= " case setNull:\n"; 706 result ~= " static if (__traits(compiles,\n"; 707 result ~= " (" ~ ClassName.stringof ~ " a)\n"; 708 result ~= " {\n"; 709 foreach(columnName; foreignKey.columnNames) 710 { 711 result ~= " a." ~ columnName ~ " = null;\n"; 712 } 713 result ~= " }))\n"; 714 result ~= " {\n"; 715 result ~= " changed" ~ foreignKey.referencedTableName ~ ".each!(\n"; 716 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 717 result ~= " {\n"; 718 foreach(columnName; foreignKey.columnNames) 719 { 720 result ~= " a." ~ columnName ~ " = null;\n"; 721 } 722 result ~= " }());\n"; 723 result ~= " break;\n"; 724 result ~= " }\n"; 725 result ~= " else\n"; 726 result ~= " {\n"; 727 result ~= " throw new ForeignKeyException(\"" ~ foreignKey.name ~ ". Cannot use Rule.setNull when the member cannot be set to null.\");\n"; 728 result ~= " }\n"; 729 result ~= " case setDefault:\n"; 730 result ~= " changed" ~ foreignKey.referencedTableName ~ ".each!(\n"; 731 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 732 result ~= " {\n"; 733 foreach(columnName; foreignKey.columnNames) 734 { 735 result ~= " static if (hasDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\"))"; 736 result ~= " {\n"; 737 result ~= " a." ~ columnName ~ " = GetDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\");\n"; 738 result ~= " }\n"; 739 result ~= " else\n"; 740 result ~= " {\n"; 741 result ~= " a." ~ columnName ~ " = typeof(a." ~ columnName ~ ").init;\n"; 742 result ~= " }\n"; 743 } 744 result ~= " }());\n"; 745 result ~= " break;\n"; 746 result ~= " case cascade:\n"; 747 result ~= " changed" ~ foreignKey.referencedTableName ~ ".each!(\n"; 748 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 749 result ~= " {\n"; 750 for (int i = 0; i < foreignKey.columnNames.length; ++i) 751 { 752 result ~= " a." ~ foreignKey.columnNames[i] ~ " = item_key." ~ foreignKey.referencedColumnNames[i] ~ ";\n"; 753 } 754 result ~= " }());\n"; 755 result ~= " break;\n"; 756 757 result ~= " }\n"; 758 result ~= " }\n"; 759 result ~= " else if (propertyName == \"remove\")\n"; 760 result ~= " {\n"; 761 // onDelete 762 result ~= " auto removed" ~ foreignKey.referencedTableName ~ " = this.byValue.filter!(\n"; 763 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 764 result ~= " {\n"; 765 result ~= " " ~ foreignKey.referencedTableName ~ ".key_type i;\n"; 766 result ~= " return (a." ~ foreignKey.name ~ "_key(i) ? i == item_key : false);\n"; 767 result ~= " }());\n"; 768 result ~= " final switch (" ~ foreignKey.name ~ "_DeleteRule) with (Rule)\n"; 769 result ~= " {\n"; 770 result ~= " case noAction:\n"; 771 result ~= " break;\n"; 772 result ~= " case restrict:\n"; 773 result ~= " if (!removed" ~ foreignKey.referencedTableName ~ ".empty)\n"; 774 result ~= " throw new ForeignKeyException(\"" ~ foreignKey.name ~ " violation.\");\n"; 775 result ~= " break;\n"; 776 result ~= " case setNull:\n"; 777 result ~= " static if (__traits(compiles,\n"; 778 result ~= " (" ~ ClassName.stringof ~ " a)\n"; 779 result ~= " {\n"; 780 foreach(columnName; foreignKey.columnNames) 781 { 782 result ~= " a." ~ columnName ~ " = null;\n"; 783 } 784 result ~= " }))\n"; 785 result ~= " {\n"; 786 result ~= " removed" ~ foreignKey.referencedTableName ~ ".each!(\n"; 787 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 788 result ~= " {\n"; 789 foreach(columnName; foreignKey.columnNames) 790 { 791 result ~= " a." ~ columnName ~ " = null;\n"; 792 } 793 result ~= " }());\n"; 794 result ~= " break;\n"; 795 result ~= " }\n"; 796 result ~= " else\n"; 797 result ~= " {\n"; 798 result ~= " throw new ForeignKeyException(\"" ~ foreignKey.name ~ ". Cannot use Rule.setNull when the member cannot be set to null.\");\n"; 799 result ~= " }\n"; 800 result ~= " case setDefault:\n"; 801 result ~= " removed" ~ foreignKey.referencedTableName ~ ".each!(\n"; 802 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 803 result ~= " {\n"; 804 foreach(columnName; foreignKey.columnNames) 805 { 806 result ~= " static if (hasDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\"))"; 807 result ~= " {\n"; 808 result ~= " a." ~ columnName ~ " = GetDefault!(" ~ ClassName.stringof ~ ", \"" ~ columnName ~ "\");\n"; 809 result ~= " }\n"; 810 result ~= " else\n"; 811 result ~= " {\n"; 812 result ~= " a." ~ columnName ~ " = typeof(a." ~ columnName ~ ").init;\n"; 813 result ~= " }\n"; 814 } 815 result ~= " }());\n"; 816 result ~= " break;\n"; 817 818 result ~= " case cascade:\n"; 819 result ~= " removed" ~ foreignKey.referencedTableName ~ ".each!(\n"; 820 result ~= " (" ~ ClassName.stringof ~ " a) =>\n"; 821 result ~= " {\n"; 822 result ~= " this.remove(a.key);\n"; 823 result ~= " }());\n"; 824 result ~= " break;\n"; 825 result ~= " }\n"; 826 result ~= " }\n"; 827 result ~= "}\n"; 828 } 829 return result; 830 } 831 } 832 833 /** 834 Confirms ClassName has exclusion constraints. 835 Returns: 836 true if ClassName has an instance of @ExclusionConstraint 837 */ 838 template hasExclusionConstraints(ClassName) 839 { 840 template Impl(T...) 841 { 842 static if (T.length == 0) 843 { 844 enum Impl = false; 845 } 846 else static if (isInstanceOf!(ExclusionConstraint, T[0])) 847 { 848 enum Impl = true; 849 } 850 else 851 { 852 alias Impl = Impl!(T[1 .. $]); 853 } 854 } 855 enum hasExclusionConstraints = Impl!(__traits(getAttributes, ClassName)); 856 } 857 858 /** 859 Gets all of the $(WIKI constraints, ExclusionConstraint) that ClassName is attributed with. If 860 the exclusion constraint name is left blank then the default name is $(D "exc_" ~ ClassName). 861 */ 862 template GetExclusionConstraints(ClassName) 863 { 864 template Impl(T...) 865 { 866 static if (T.length == 0) 867 { 868 alias Impl = AliasSeq!(); 869 } 870 else static if (isInstanceOf!(ExclusionConstraint, T[0])) 871 { 872 static if (T[0].name == "") 873 { 874 enum name = "exc_" ~ ClassName.stringof; 875 alias R = ExclusionConstraint!(T[0].exclusion, name); 876 alias Impl = AliasSeq!(R, Impl!(T[1 .. $])); 877 } 878 else 879 { 880 alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $])); 881 } 882 } 883 else 884 { 885 alias Impl = Impl!(T[1 .. $]); 886 } 887 } 888 alias GetExclusionConstraints = Impl!(__traits(getAttributes, ClassName)); 889 }